jq コマンドに大きな数値を与えると丸め誤差が発生する!
はじめに
こんにちは、川原です。
ここ最近ずっと、JSON をレスポンスするAPIサーバープログラムの開発に携わっており、JSON と戯れる日々を送っています。
そうなると、JSON レスポンスの確認するために jq
をよく使うことになるのですが、先日、jq
起因のとある事象にハマりましたので、その事象を共有します。
結論
まず、結論ですが、
jq
に大きな数値を与えると、「丸め誤差」が発生します。
以下のように Java の Long.MAX_VALUE 値である 9223372036854775807
を jq
に与えてみると、、、
>jq -V jq-1.5 >echo '{ "BigInteger": 9223372036854775807 }' | jq . { "BigInteger": 9223372036854776000 }
下4桁の部分で丸められて(切り上げられて)しまいます。
上記例のように、数値を jq
に与え、jq
の出力をそのまま確認している分には、想定外の値が出力される問題事象の原因が jq
であることにすぐに気付けます。
が、私の場合、APIサーバーの出力内容を jq
経由で確認している際に、この事象(想定と異なる数値の出力)に遭遇しました。そのため、APIサーバー側の処理の中で数値を丸めてJSONとして出力しているのではないかと、APIサーバー側のプログラムソースコードをずっと確認してしまいました。。。(;^_^
(jq
の処理は与えられた JSON を単純に整形して出力するだけ、という思い込みもありました)
本事象について jq
の開発元の情報を確認してみると、ちゃんと記載されていました。
https://github.com/stedolan/jq/wiki/FAQ#numbers
Q: What are the largest (smallest) numbers that jq can handle? Does "overflow" cause an error?
A: Currently, jq does not include "bigint" support. Very large integers will be converted to a floating point approximation. The largest number that can be reliably used as an integer is 2^53 (9,007,199,254,740,992). The largest floating point value is about 1.79e+308 and the smallest is about 1e-323.
In general, arithmetic operations do not raise errors, except that in jq 1.5, division by 0 does result in an error. In jq 1.4, 1/0 prints out as 1.7976931348623157e+308, and 0 * (1/0) evaluates to null.
A basic "bigint" library is available at Bigint.jq.
上記のように jq
では、整数値は 2^53 (9,007,199,254,740,992)
までしか扱えないようです。
ということで、jq
に大きな数値を与えるときは気をつけましょう。
また、APIサーバーの JSON レスポンスが想定外の値である場合は、jq
を介さない素のレスポンスも確認しましょう。